/**
 * \file    grammar.cpp
 * \brief   Parser generator.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    September 17, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#include "symbol.h"
#include "rule.h"
#include "grammar.h"

LPP_NAMESPACE_BEGIN

SymbolRef const & Grammar::symbol_at(Index const & i) {
    for (SymbolRef const & symbol: symbols_m) {
        if (symbol->index() == i) {
            return symbol;
        }
    }
#ifdef WITH_ERROR_SYMBOL
    return error_symbol();
#else
    throw std::logic_error("Symbol index out of bounds");
#endif
}

SymbolRef const & Grammar::symbol_for_shortcut(const String & shortcut) {
    return symbol_for_name(shortcut_map_m.at(shortcut));
}

SymbolRef const & Grammar::symbol_for_name(const String & name) const {
    if (symbol_map_m.count(name)) {
        return symbol_map_m.at(name);
    }
    static SymbolRef S;
    return S;
};

SymbolRef & Grammar::symbol_for_name(const String & name, const String & shortcut) {
#ifdef WITH_ERROR_SYMBOL
    if ("!" == name) {
        use_error_symbol_m = true;
    }
#endif
    if (symbol_map_m.count(name)) {
        return symbol_map_m.at(name);
    }
    auto ans = symbol_map_m.emplace(name,Symbol::Make(number_of_symbols(),*this,name,shortcut));
    if (!shortcut.empty()) {
        shortcut_map_m[shortcut] = name;
    }
    SymbolRef & S = ans.first->second;
    symbols_m.push_back(S);
    if (S->is_terminal()) {
        terminals_m.push_back(S);
        lookahead_symbols_m.push_back(S);
    } else if (S->is_non_terminal()) {
        non_terminals_m.push_back(S);
        if (!S->is_accept()) {
            lookahead_symbols_m.push_back(S);
        }
    }
    return S;
};

void Grammar::wildcard_symbol(String const & __n) {
    wildcard_symbol_m = symbol_for_name(__n);
}

void Grammar::set_start_symbol_name(String const & __n) {
    start_symbol_m = symbol_for_name(__n);
}

/* Find a precedence symbol of every rule in the grammar.
**
** Those rules which have a precedence symbol coded in the input
** grammar using the "[symbol]" construct will already have the
** rule->precedence_symbol_m field filled.  Other rules take as their precedence
** symbol the first RHS symbol with a defined precedence.  If there
** are not RHS symbols with a defined precedence, the precedence
** symbol field is left blank.
*/
void Grammar::setup_rule_precedences() {
    for (auto & rule: rules_m) {
        if (!rule->precedence_symbol()) {
            for (auto & rhs: rule->rhs_symbols()) {
                if  (rhs->is_multi_terminal()) {
                    for (SymbolRef const & sR: rhs->get_sub_symbols()) {
                        if (sR && sR->has_precedence()) {
                            rule->precedence_symbol(sR);
                            break;
                        }
                    }
                } else if  (rhs->has_precedence()) {
                    rule->precedence_symbol(rhs);
                    break;
                }
            }
        }
    }
    return;
}

RuleRef Grammar::new_rule(SymbolRef const & lhs
                        , String const & alias
                        , Index rule_line) {
    return Rule::Make(rules_m.size()+1,*this,lhs,alias,rule_line);
}

bool Grammar::add_rule(const RuleRef &rule) {
#if 1
    if (rule->lhs_symbol()->is_accept()) {
        rule->id(0);
        rule->index(0);
    }
    if (rules_m.insert(rule).second) {
        rule->lhs_symbol()->add_rule(rule);
        DEBUG_MESSAGE("NEW RULE: %s",Description()(rule).c_str());
        return true;
    }
    return false;
#else
    bool should_add = true;
    for (RuleRef const & R: rules_m) {
        if (R==rule) {
            should_add = false;
            break;
        }
    }
    if (should_add) {
        rule->id(rules_m.size());
        rules_m.push_back(rule);
        rule->lhs_symbol()->add_rule(rule);
    }
    return should_add;
#endif
}

bool Grammar::promote_to_multi_terminal(String const & name) {
    SymbolRef const & symbol = symbol_for_name(name);
    if (symbol->is_multi_terminal()) {
        return false;
    }
    if (symbol->is_terminal()) {
        multi_terminals_m.push_front(symbol);
        terminals_m.erase(std::remove_if(terminals_m.begin(),
                                         terminals_m.end(),
                                         [symbol](SymbolRef const  & s) { return s == symbol; }),
                terminals_m.end());
        lookahead_symbols_m.erase(std::remove_if(lookahead_symbols_m.begin(),
                                         lookahead_symbols_m.end(),
                                         [symbol](SymbolRef const  & s) { return s == symbol; }),
                lookahead_symbols_m.end());
        return true;
    } else if (symbol->is_non_terminal()) {
        multi_terminals_m.push_front(symbol);
        non_terminals_m.erase(std::remove_if(non_terminals_m.begin(),
                                         non_terminals_m.end(),
                                         [symbol](SymbolRef const  & s) { return s == symbol; }),
                non_terminals_m.end());
        lookahead_symbols_m.erase(std::remove_if(lookahead_symbols_m.begin(),
                                         lookahead_symbols_m.end(),
                                         [symbol](SymbolRef const  & s) { return s == symbol; }),
                lookahead_symbols_m.end());
        return true;
    }
    return false;
}
bool Grammar::promote_to_default(String const & name) {
    SymbolRef const & symbol = symbol_for_name(name);
    if (symbol->is_default()) {
        return false;
    }
    if (symbol->is_terminal()) {
        terminals_m.erase(std::remove_if(terminals_m.begin(),
                                         terminals_m.end(),
                                         [symbol](SymbolRef const  & s) { return s == symbol; }),
                terminals_m.end());
        return true;
    } else if (symbol->is_non_terminal()) {
        non_terminals_m.erase(std::remove_if(non_terminals_m.begin(),
                                         non_terminals_m.end(),
                                         [symbol](SymbolRef const  & s) { return s == symbol; }),
                non_terminals_m.end());
        return true;
    }
    return false;
}

LPP_NAMESPACE_END
